const ASRClient = require('../asrClient');
const WebSocket = require('ws');

// Mock WebSocket
jest.mock('ws');

describe('ASRClient 状态机深度测试', () => {
  let client;
  let mockWs;

  beforeEach(() => {
    jest.clearAllMocks();
    
    mockWs = {
      on: jest.fn(),
      send: jest.fn(),
      close: jest.fn(),
      readyState: WebSocket.OPEN
    };
    
    WebSocket.mockImplementation(() => mockWs);
    client = new ASRClient('ws://test:8888');
    
    // 重置状态到初始状态，因为构造函数会调用connect()
    client.currentState = 'disconnected';
    client.stateHistory = [];
    client.stateChangeCallbacks.clear();
  });

  describe('状态转换矩阵完整性测试', () => {
    const STATES = {
      DISCONNECTED: 'disconnected',
      CONNECTING: 'connecting', 
      CONNECTED: 'connected',
      RECONNECTING: 'reconnecting',
      IDLE: 'idle',
      AUDIO_STREAMING: 'audio_streaming',
      PROCESSING_AUDIO: 'processing_audio',
      FILE_UPLOADING: 'file_uploading',
      PROCESSING_FILE: 'processing_file',
      ERROR: 'error'
    };

    const STATE_TRANSITIONS = {
      [STATES.DISCONNECTED]: [STATES.CONNECTING, STATES.ERROR],
      [STATES.CONNECTING]: [STATES.CONNECTED, STATES.DISCONNECTED, STATES.ERROR],
      [STATES.CONNECTED]: [STATES.IDLE, STATES.DISCONNECTED, STATES.RECONNECTING, STATES.ERROR],
      [STATES.RECONNECTING]: [STATES.CONNECTING, STATES.DISCONNECTED, STATES.ERROR],
      [STATES.IDLE]: [STATES.AUDIO_STREAMING, STATES.FILE_UPLOADING, STATES.DISCONNECTED, STATES.ERROR],
      [STATES.AUDIO_STREAMING]: [STATES.PROCESSING_AUDIO, STATES.IDLE, STATES.DISCONNECTED, STATES.ERROR],
      [STATES.PROCESSING_AUDIO]: [STATES.IDLE, STATES.DISCONNECTED, STATES.ERROR],
      [STATES.FILE_UPLOADING]: [STATES.PROCESSING_FILE, STATES.IDLE, STATES.DISCONNECTED, STATES.ERROR],
      [STATES.PROCESSING_FILE]: [STATES.IDLE, STATES.DISCONNECTED, STATES.ERROR],
      [STATES.ERROR]: [STATES.DISCONNECTED, STATES.CONNECTING]
    };

    test('应该测试所有定义的状态转换', () => {
      Object.keys(STATE_TRANSITIONS).forEach(fromState => {
        STATE_TRANSITIONS[fromState].forEach(toState => {
          expect(client.isValidStateTransition(fromState, toState)).toBe(true);
        });
      });
    });

    test('应该拒绝所有未定义的状态转换', () => {
      const allStates = Object.values(STATES);
      
      allStates.forEach(fromState => {
        allStates.forEach(toState => {
          const allowedTransitions = STATE_TRANSITIONS[fromState] || [];
          if (!allowedTransitions.includes(toState)) {
            expect(client.isValidStateTransition(fromState, toState)).toBe(false);
          }
        });
      });
    });

    test('应该测试每个状态都有定义的转换规则', () => {
      const allStates = Object.values(STATES);
      allStates.forEach(state => {
        expect(STATE_TRANSITIONS).toHaveProperty(state);
        expect(Array.isArray(STATE_TRANSITIONS[state])).toBe(true);
      });
    });
  });

  describe('状态转换路径测试', () => {
    test('应该支持正常的连接建立路径', () => {
      const path = ['disconnected', 'connecting', 'connected', 'idle'];
      
      for (let i = 0; i < path.length - 1; i++) {
        client.currentState = path[i];
        expect(client.setState(path[i + 1])).toBe(true);
      }
      
      expect(client.getCurrentState()).toBe('idle');
    });

    test('应该支持音频处理完整路径', () => {
      // 先到达idle状态
      client.currentState = 'disconnected';
      client.setState('connecting');
      client.currentState = 'connecting';
      client.setState('connected');
      client.currentState = 'connected';
      client.setState('idle');
      
      // 音频处理路径：idle -> audio_streaming -> processing_audio -> idle
      client.currentState = 'idle';
      expect(client.setState('audio_streaming')).toBe(true);
      client.currentState = 'audio_streaming';
      expect(client.setState('processing_audio')).toBe(true);
      client.currentState = 'processing_audio';
      expect(client.setState('idle')).toBe(true);
      
      expect(client.getCurrentState()).toBe('idle');
    });

    test('应该支持文件处理完整路径', () => {
      // 先到达idle状态
      client.currentState = 'disconnected';
      client.setState('connecting');
      client.currentState = 'connecting';
      client.setState('connected');
      client.currentState = 'connected';
      client.setState('idle');
      
      // 文件处理路径：idle -> file_uploading -> processing_file -> idle
      client.currentState = 'idle';
      expect(client.setState('file_uploading')).toBe(true);
      client.currentState = 'file_uploading';
      expect(client.setState('processing_file')).toBe(true);
      client.currentState = 'processing_file';
      expect(client.setState('idle')).toBe(true);
      
      expect(client.getCurrentState()).toBe('idle');
    });

    test('应该支持错误恢复路径', () => {
      client.currentState = 'idle';
      
      // 进入错误状态
      expect(client.setState('error')).toBe(true);
      
      // 从错误状态恢复
      expect(client.setState('disconnected')).toBe(true);
      expect(client.setState('connecting')).toBe(true);
    });

    test('应该支持重连路径', () => {
      client.currentState = 'connected';
      
      // 连接断开触发重连
      expect(client.setState('reconnecting')).toBe(true);
      expect(client.setState('connecting')).toBe(true);
      expect(client.setState('connected')).toBe(true);
    });
  });

  describe('并发状态变化测试', () => {
    test('应该正确处理快速连续的状态变化', () => {
      const stateChanges = [];
      client.onStateChange((from, to) => {
        stateChanges.push({ from, to, timestamp: Date.now() });
      });
      
      // 手动设置状态以避免无效转换
      client.currentState = 'disconnected';
      client.setState('connecting');
      client.currentState = 'connecting';
      client.setState('connected');
      client.currentState = 'connected';
      client.setState('idle');
      client.currentState = 'idle';
      client.setState('audio_streaming');
      client.currentState = 'audio_streaming';
      client.setState('processing_audio');
      client.currentState = 'processing_audio';
      client.setState('idle');
      
      expect(stateChanges).toHaveLength(6);
      expect(client.getCurrentState()).toBe('idle');
      
      // 验证状态变化的顺序
      const expectedSequence = [
        'connecting', 'connected', 'idle', 'audio_streaming', 'processing_audio', 'idle'
      ];
      
      stateChanges.forEach((change, index) => {
        expect(change.to).toBe(expectedSequence[index]);
      });
    });

    test('应该正确处理状态变化回调中的异常', () => {
      let callbackCount = 0;
      
      // 第一个回调会抛出异常
      client.onStateChange(() => {
        callbackCount++;
        throw new Error('Callback error');
      });
      
      // 第二个回调正常执行
      client.onStateChange(() => {
        callbackCount++;
      });
      
      const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
      
      // 确保从有效状态开始转换
      client.currentState = 'disconnected';
      client.setState('connecting');
      
      expect(callbackCount).toBe(2);
      expect(client.getCurrentState()).toBe('connecting');
      expect(consoleSpy).toHaveBeenCalled();
      
      consoleSpy.mockRestore();
    });
  });

  describe('状态历史和追踪测试', () => {
    test('应该正确记录状态变化的时间戳', () => {
      const startTime = Date.now();
      
      client.setState('connecting');
      client.setState('connected');
      
      const history = client.getStateHistory();
      
      expect(history).toHaveLength(2);
      history.forEach(entry => {
        expect(entry.timestamp.getTime()).toBeGreaterThanOrEqual(startTime);
        expect(entry.timestamp.getTime()).toBeLessThanOrEqual(Date.now());
      });
    });

    test('应该正确记录状态变化的数据', () => {
      const testData1 = { type: 'connection', id: 1 };
      const testData2 = { type: 'message', content: 'test' };
      
      // 确保从有效状态开始
      client.currentState = 'disconnected';
      client.setState('connecting', testData1);
      client.currentState = 'connecting';
      client.setState('connected', testData2);
      
      const history = client.getStateHistory();
      
      expect(history[0].data).toEqual(testData1);
      expect(history[1].data).toEqual(testData2);
    });

    test('应该正确维护状态历史的大小限制', () => {
      // 清空现有历史
      client.stateHistory = [];
      
      // 添加超过限制的状态变化
      for (let i = 0; i < 105; i++) {
        client.stateHistory.push({
          from: 'idle',
          to: 'audio_streaming',
          timestamp: new Date(),
          data: { index: i }
        });
      }
      
      // 添加新的状态变化，这应该触发历史限制
      client.setState('connecting');
      
      const history = client.getStateHistory();
      expect(history.length).toBeLessThanOrEqual(100);
      
      // 验证最新的状态变化在历史中
      const lastEntry = history[history.length - 1];
      expect(lastEntry.to).toBe('connecting');
    });

    test('应该支持状态历史的深拷贝', () => {
      client.setState('connecting');
      
      const history1 = client.getStateHistory();
      const history2 = client.getStateHistory();
      
      // 验证是不同的数组实例
      expect(history1).not.toBe(history2);
      
      // 修改一个历史记录不应影响另一个
      if (history1.length > 0) {
        history1[0].modified = true;
        expect(history2[0]).not.toHaveProperty('modified');
      }
    });
  });

  describe('状态查询和条件测试', () => {
    test('应该正确判断复合状态条件', () => {
      // 测试连接相关状态
      const connectedStates = ['connected', 'idle', 'audio_streaming', 'processing_audio', 'file_uploading', 'processing_file'];
      
      connectedStates.forEach(state => {
        client.currentState = state;
        expect(client.isConnected()).toBe(true);
      });
      
      const disconnectedStates = ['disconnected', 'connecting', 'reconnecting', 'error'];
      
      disconnectedStates.forEach(state => {
        client.currentState = state;
        expect(client.isConnected()).toBe(false);
      });
    });

    test('应该正确判断处理状态', () => {
      const processingStates = ['audio_streaming', 'processing_audio', 'file_uploading', 'processing_file'];
      
      processingStates.forEach(state => {
        client.currentState = state;
        expect(client.isProcessing()).toBe(true);
      });
      
      const nonProcessingStates = ['disconnected', 'connecting', 'connected', 'idle', 'error'];
      
      nonProcessingStates.forEach(state => {
        client.currentState = state;
        expect(client.isProcessing()).toBe(false);
      });
    });

    test('应该支持自定义状态查询', () => {
      // 测试是否可以执行特定操作
      const testCases = [
        { state: 'idle', action: 'selectMode', expected: true },
        { state: 'audio_streaming', action: 'selectMode', expected: false },
        { state: 'idle', action: 'sendAudio', expected: true },
        { state: 'audio_streaming', action: 'sendAudio', expected: true },
        { state: 'processing_audio', action: 'sendAudio', expected: false },
        { state: 'audio_streaming', action: 'endAudio', expected: true },
        { state: 'idle', action: 'endAudio', expected: false }
      ];
      
      testCases.forEach(({ state, action, expected }) => {
        client.currentState = state;
        expect(client.canPerformAction(action)).toBe(expected);
      });
    });
  });

  describe('错误状态和恢复测试', () => {
    test('应该正确处理各种错误场景', () => {
      const errorScenarios = [
        { fromState: 'connecting', errorData: { type: 'connection_timeout' } },
        { fromState: 'audio_streaming', errorData: { type: 'audio_error' } },
        { fromState: 'file_uploading', errorData: { type: 'upload_error' } }
      ];
      
      errorScenarios.forEach(({ fromState, errorData }) => {
        client.currentState = fromState;
        
        expect(client.setState('error', errorData)).toBe(true);
        expect(client.hasError()).toBe(true);
        
        const history = client.getStateHistory();
        const lastEntry = history[history.length - 1];
        expect(lastEntry.data).toEqual(errorData);
        
        // 测试错误恢复
        expect(client.resetError()).toBe(true);
        expect(client.getCurrentState()).toBe('disconnected');
        expect(client.hasError()).toBe(false);
      });
    });

    test('应该支持从错误状态的不同恢复路径', () => {
      client.currentState = 'error';
      
      // 路径1: 直接重连
      expect(client.setState('connecting')).toBe(true);
      
      client.currentState = 'error';
      
      // 路径2: 先断开再连接
      expect(client.setState('disconnected')).toBe(true);
      expect(client.setState('connecting')).toBe(true);
    });
  });

  describe('状态机性能和稳定性测试', () => {
    test('应该处理大量状态变化而不影响性能', () => {
      const startTime = Date.now();
      const iterations = 1000;
      
      for (let i = 0; i < iterations; i++) {
        client.currentState = 'idle';
        client.setState('audio_streaming');
        client.setState('processing_audio');
        client.setState('idle');
      }
      
      const endTime = Date.now();
      const duration = endTime - startTime;
      
      // 应该在合理时间内完成（假设1秒内）
      expect(duration).toBeLessThan(1000);
      expect(client.getCurrentState()).toBe('idle');
    });

    test('应该正确处理状态回调的内存管理', () => {
      const callbacks = [];
      
      // 注册大量回调
      for (let i = 0; i < 100; i++) {
        const id = client.onStateChange(() => {});
        callbacks.push(id);
      }
      
      expect(client.stateChangeCallbacks.size).toBe(100);
      
      // 移除一半回调
      for (let i = 0; i < 50; i++) {
        client.offStateChange(callbacks[i]);
      }
      
      expect(client.stateChangeCallbacks.size).toBe(50);
      
      // 触发状态变化，确保剩余回调正常工作
      client.currentState = 'disconnected';
      client.setState('connecting');
      expect(client.getCurrentState()).toBe('connecting');
    });

    test('应该处理状态变化中的循环引用', () => {
      let changeCount = 0;
      
      // 创建可能导致循环的回调
      client.onStateChange((from, to) => {
        changeCount++;
        // 防止无限循环
        if (changeCount < 5 && to === 'connecting') {
          // 这不会触发新的状态变化，因为已经在connecting状态
          client.setState('connecting');
        }
      });
      
      client.currentState = 'disconnected';
      client.setState('connecting');
      
      expect(changeCount).toBe(1);
      expect(client.getCurrentState()).toBe('connecting');
    });
  });

  describe('状态机边界条件测试', () => {
    test('应该处理null和undefined状态', () => {
      // 这些应该返回false或者被安全处理
      const result1 = client.isValidStateTransition(null, 'connecting');
      const result2 = client.isValidStateTransition('disconnected', null);
      const result3 = client.isValidStateTransition(undefined, 'connecting');
      
      // 如果返回undefined，说明没有正确处理边界情况
      expect(result1).toBeDefined();
      expect(result2).toBeDefined();
      expect(result3).toBeDefined();
      
      // 应该返回false
      expect(result1).toBe(false);
      expect(result2).toBe(false);
      expect(result3).toBe(false);
    });

    test('应该处理空字符串状态', () => {
      const result1 = client.isValidStateTransition('', 'connecting');
      const result2 = client.isValidStateTransition('disconnected', '');
      
      expect(result1).toBeDefined();
      expect(result2).toBeDefined();
      expect(result1).toBe(false);
      expect(result2).toBe(false);
    });

    test('应该处理不存在的状态', () => {
      const result1 = client.isValidStateTransition('nonexistent', 'connecting');
      const result2 = client.isValidStateTransition('disconnected', 'nonexistent');
      
      expect(result1).toBeDefined();
      expect(result2).toBeDefined();
      expect(result1).toBe(false);
      expect(result2).toBe(false);
    });

    test('应该处理相同状态的转换', () => {
      // 大多数状态不应该允许转换到自身
      expect(client.isValidStateTransition('idle', 'idle')).toBe(false);
      expect(client.isValidStateTransition('connecting', 'connecting')).toBe(false);
    });
  });
});